//
// SoapTrace.Net - A soap message monitoring utility. 
//
// Copyright (C) 2005 Matthew Ward
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
// Matthew Ward (mrward@users.sourceforge.net)

using SoapTrace.Core.Diagnostics;
using System;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace SoapTrace.Core.Communication
{
	/// <summary>
	/// Web Service Client.
	/// </summary>
	public class WebServiceClient : TcpSocketConnection
	{
		/// <summary>
		/// Triggered when the soap request has finally been sent.
		/// </summary>
		public event SoapRequestMessageReceivedEventHandler SoapRequestMessageSent;
		
		/// <summary>
		/// Triggered when a soap response has been received by the client.
		/// </summary>
		public event SoapResponseMessageReceivedEventHandler SoapResponseMessageReceived;
		
		/// <summary>
		/// The client id.
		/// </summary>
		private ClientId m_Id = new ClientId( );
		
		/// <summary>
		/// The main processing thread.
		/// </summary>
		private Thread m_Thread;
		
		/// <summary>
		/// Flag indicating whether the main processing thread should
		/// run.
		/// </summary>
		private bool m_Running;
		
		/// <summary>
		/// Socket processing timeout.
		/// </summary>
		private int m_Timeout = 10;		
		
		/// <summary>
		/// The soap request associated with this client.
		/// </summary>
		private SoapRequest m_Request = new SoapRequest( );
		
		/// <summary>
		/// The soap response associated with this client.
		/// </summary>
		private SoapResponse m_Response = new SoapResponse( );
		
		/// <summary>
		/// Creates a new instance of the <see cref="WebServiceClient"/>.
		/// </summary>
		public WebServiceClient( )
		{
		}
		
		/// <summary>
		/// Queues the soap request for sending.
		/// </summary>
		/// <param name="request">A soap request.</param>
		public void Send( SoapRequest request )
		{
			Send( String.Concat( request.Header, request.Message ) );
		}
		
		/// <summary>
		/// Sends the soap request.
		/// </summary>
		/// <param name="message">A soap request.</param>
		public void Send( string message )
		{
			AddMessage( message );

			if( m_Running == false )
			{
				StartProcessingThread( );
			}			
		}
		
		/// <summary>
		/// Shutdown the web service client.
		/// </summary>
		public void Shutdown( )
		{
			StopProcessingThread( );
			Close( );
		}
		
		/// <summary>
		/// Raises the SoapRequestMessageSent event.
		/// </summary>
		/// <param name="request">A soap request.</param>
		protected void OnSoapRequestMessageSent( SoapRequest request )
		{
			if( SoapRequestMessageSent != null )
			{
				SoapRequestMessageSent( this, new SoapRequestReceivedEventArgs( m_Id, request ) );
			}
		}
		
		/// <summary>
		/// Raises the SoapResponseMessageReceived event.
		/// </summary>
		/// <param name="response">A soap response.</param>
		protected void OnSoapResponseMessageReceived( SoapResponse response )
		{
			if( SoapResponseMessageReceived != null )
			{
				SoapResponseMessageReceived( this, new SoapResponseReceivedEventArgs( m_Id, response ) );
			}
		}		
		
		/// <summary>
		/// Bytes received from the remote end point.
		/// </summary>
		/// <param name="bytes">A byte array.</param>
		/// <param name="length">The number of bytes received.</param>
		protected override void BytesReceived( byte[ ] bytes, int length )
		{
			for( int i = 0; i < length; ++i )
			{
				if( m_Response.AddByte( bytes[ i ] ) )
				{
					m_Response.TimeReceived = DateTime.Now;
					m_Response.Source = LocalEndPoint.ToString( );

					OnSoapResponseMessageReceived( m_Response );

					m_Response = new SoapResponse( );
				}
			}	
		}
		
		/// <summary>
		/// Bytes sent to the remote end point.
		/// </summary>
		/// <param name="bytes">A byte array.</param>
		/// <param name="length">The number of bytes sent.</param>
		protected override void BytesSent( byte[ ] bytes, int length )
		{
			for( int i = 0; i < length; ++i )
			{
				if( m_Request.AddByte( bytes[ i ] ) )
				{
					m_Request.TimeReceived = DateTime.Now;
					m_Request.Source = LocalEndPoint.ToString( );

					OnSoapRequestMessageSent( m_Request );

					m_Request = new SoapRequest( );
				}
			}							
		}
		
		/// <summary>
		/// Creates a soap response object.
		/// </summary>
		SoapResponse CreateSoapResponse( string message )
		{
			SoapResponse Response = new SoapResponse( );
			
			AddMessage( Response, message );			
			
			return Response;
		}		
		
		/// <summary>
		/// Adds the message text to a soap message object.
		/// </summary>
		/// <param name="message">A soap message object.</param>
		/// <param name="messageText">The message text.</param>
		private void AddMessage( SoapMessage message, string messageText )
		{
			byte[ ] Bytes = UnicodeEncoding.UTF8.GetBytes( messageText );
			
			foreach( byte CurrentByte in Bytes )
			{
				message.AddByte( CurrentByte );
			}
		}
		
		/// <summary>
		/// Adds a soap message bytes to the tcp connection
		/// for sending.
		/// </summary>
		/// <param name="message">A soap message.</param>
		private void AddMessage( string message )
		{
			byte[ ] Bytes = UnicodeEncoding.UTF8.GetBytes( message );

			AddBytes( Bytes, Bytes.Length );
		}		
		
		/// <summary>
		/// Starts the main socket processing thread.
		/// </summary>
		private void StartProcessingThread( )
		{
			m_Thread = new Thread( new ThreadStart( ProcessConnection ) );
			m_Running = true;
			m_Thread.Start( );
		}	
		
		/// <summary>
		/// Stops the socket processing thread.
		/// </summary>
		private void StopProcessingThread( )
		{
			if( m_Thread != null )
			{
				m_Running = false;
				m_Thread.Join( );
				m_Thread = null;
			}
		}	
		
		/// <summary>
		/// Processes the connected client socket.
		/// </summary>
		private void ProcessConnection( )
		{
			while( m_Running )
			{
				try
				{
					// Send any data.
					
					bool Open = Read( );
		
					if( Open )
					{		
						// Sends any data from the client to the server.
		
						Send( );
						
						// Make sure this thread does not hog the
						// CPU.
	
						Thread.Sleep( m_Timeout );	
					}
					else
					{
						// Shutdown.
						Close( );
						m_Running = false;
					}
				}
				catch( SocketException Ex )
				{
					Logger.LogError( String.Concat( "WebServiceClient::ProcessConnection ",
					                               Ex.ToString( ) ) );
				}
			}
		}		
	}
}
